﻿using System;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Net;
using System.ServiceModel;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Discovery;

namespace PluginRegistrationTool
{
    /// <summary>
    /// Dynamics CRM Server Connection Class.
    /// 
    /// This class provides internal constructors, properties and methods for establishing a WCF connection with a Dynamics CRM Server by providing the relevant parameters.
    /// </summary>
    internal class ServerConnection
    {
        #region Constructors

        /// <summary>
        /// ServerConnection Class Constructor.
        /// </summary>
        /// <param name="serverAddress">Dynamics CRM Server address.</param>
        /// <param name="organizationName">Dynamics CRM organisation name.</param>
        /// <param name="isSecure">if set to <c>true</c> [is secure].</param>
        internal ServerConnection(String serverAddress, String organizationName, bool isSecure)
        {
            try
            {
                if (isSecure)
                {
                    UriScheme = Uri.UriSchemeHttps;
                }
                else
                {
                    UriScheme = Uri.UriSchemeHttp;
                }
                this.Config.ServerAddress = serverAddress;
                this.Config.OrganizationName = organizationName;
                this.Config.DiscoveryUri = new Uri(String.Format(CultureInfo.CurrentCulture, "{0}://{1}/XRMServices/2011/Discovery.svc", UriScheme, this.Config.ServerAddress));
                this.Config.Credentials = new ClientCredentials();
                this.Config.Credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
                this.Config.DeviceCredentials = new ClientCredentials();
                this.Config.DeviceCredentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
                this.Config.OrganizationUri = GetOrganizationAddress(this.Config.DiscoveryUri);
                if (this.Configurations == null)
                {
                    this.Configurations = new Collection<Configuration>();
                    this.Configurations.Add(this.Config);
                }
                this.Configuration = this.Config;
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
            {
                throw;
            }
        }

        /// <summary>
        /// ServerConnection Class Constructor overloaded.
        /// </summary>
        /// <param name="serverAddress">Dynamics CRM Server address.</param>
        /// <param name="organizationName">Dynamics CRM organisation name.</param>
        /// <param name="domain">AD Domain.</param>
        /// <param name="userName">User name.</param>
        /// <param name="password">Password.</param>
        /// <param name="isSecure">if set to <c>true</c> [is secure].</param>
        internal ServerConnection(String serverAddress, String organizationName, string domain, string userName, string password, bool isSecure)
        {
            try
            {
                if (isSecure)
                {
                    UriScheme = Uri.UriSchemeHttps;
                }
                else
                {
                    UriScheme = Uri.UriSchemeHttp;
                }
                this.Config.ServerAddress = serverAddress;
                this.Config.OrganizationName = organizationName;
                this.Config.DiscoveryUri = new Uri(String.Format(CultureInfo.CurrentCulture, "{0}://{1}/XRMServices/2011/Discovery.svc", UriScheme, this.Config.ServerAddress));
                this.Config.Credentials = new ClientCredentials();
                this.Config.Credentials.Windows.ClientCredential = new NetworkCredential();
                this.Config.Credentials.Windows.ClientCredential.Domain = domain;
                this.Config.Credentials.Windows.ClientCredential.UserName = userName;
                this.Config.Credentials.Windows.ClientCredential.Password = password;
                this.Config.DeviceCredentials = new ClientCredentials();
                this.Config.DeviceCredentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
                this.Config.OrganizationUri = GetOrganizationAddress(this.Config.DiscoveryUri);
                if (this.Configurations == null)
                {
                    this.Configurations = new Collection<Configuration>();
                    this.Configurations.Add(this.Config);
                }
                this.Configuration = this.Config;
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
            {
                throw;
            }
        }

        /// <summary>
        /// ServerConnection Class Constructor overloaded.
        /// </summary>
        /// <param name="serverAddress">Dynamics CRM Server address.</param>
        /// <param name="organizationName">Dynamics CRM organisation name.</param>
        /// <param name="deviceDomain">Device AD Domain.</param>
        /// <param name="deviceUserName">Device user name.</param>
        /// <param name="devicePassword">Device password.</param>
        /// <param name="useDeviceCredentials">Boolean - use device credentials?</param>
        /// <param name="isSecure">if set to <c>true</c> [is secure].</param>
        internal ServerConnection(String serverAddress, String organizationName, string deviceDomain, string deviceUserName, string devicePassword, bool useDeviceCredentials, bool isSecure)
        {
            try
            {
                if (isSecure)
                {
                    UriScheme = Uri.UriSchemeHttps;
                }
                else
                {
                    UriScheme = Uri.UriSchemeHttp;
                }
                this.Config.ServerAddress = serverAddress;
                this.Config.OrganizationName = organizationName;
                this.Config.DiscoveryUri = new Uri(String.Format(CultureInfo.CurrentCulture, "{0}://{1}/XRMServices/2011/Discovery.svc", UriScheme, this.Config.ServerAddress));
                this.Config.Credentials = new ClientCredentials();
                this.Config.Credentials.Windows.ClientCredential = CredentialCache.DefaultNetworkCredentials;
                if (useDeviceCredentials)
                {
                    this.Config.DeviceCredentials = new ClientCredentials();
                    this.Config.DeviceCredentials.Windows.ClientCredential = new NetworkCredential();
                    this.Config.DeviceCredentials.Windows.ClientCredential.Domain = deviceDomain;
                    this.Config.DeviceCredentials.Windows.ClientCredential.UserName = deviceUserName;
                    this.Config.DeviceCredentials.Windows.ClientCredential.Password = devicePassword;
                }
                this.Config.OrganizationUri = GetOrganizationAddress(this.Config.DiscoveryUri);
                if (this.Configurations == null)
                {
                    this.Configurations = new Collection<Configuration>();
                    this.Configurations.Add(this.Config);
                }
                this.Configuration = this.Config;
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
            {
                throw;
            }
        }

        /// <summary>
        /// ServerConnection Class Constructor overloaded.
        /// </summary>
        /// <param name="serverAddress">Dynamics CRM Server address.</param>
        /// <param name="organizationName">Dynamics CRM Organisation name.</param>
        /// <param name="domain">AD Domain.</param>
        /// <param name="userName">User name.</param>
        /// <param name="password">Password.</param>
        /// <param name="deviceDomain">Device AD Domain.</param>
        /// <param name="deviceUserName">Device user name.</param>
        /// <param name="devicePassword">Device password.</param>
        /// <param name="isSecure">if set to <c>true</c> [is secure].</param>
        internal ServerConnection(String serverAddress, String organizationName, string domain, string userName, string password, string deviceDomain, string deviceUserName, string devicePassword, bool isSecure)
        {
            try
            {
                if (isSecure)
                {
                    UriScheme = Uri.UriSchemeHttps;
                }
                else
                {
                    UriScheme = Uri.UriSchemeHttp;
                }
                this.Config.ServerAddress = serverAddress;
                this.Config.OrganizationName = organizationName;
                this.Config.DiscoveryUri = new Uri(String.Format(CultureInfo.CurrentCulture, "{0}://{1}/XRMServices/2011/Discovery.svc", UriScheme, this.Config.ServerAddress));
                this.Config.Credentials = new ClientCredentials();
                this.Config.Credentials.Windows.ClientCredential = new NetworkCredential();
                this.Config.Credentials.Windows.ClientCredential.Domain = domain;
                this.Config.Credentials.Windows.ClientCredential.UserName = userName;
                this.Config.Credentials.Windows.ClientCredential.Password = password;
                this.Config.DeviceCredentials = new ClientCredentials();
                this.Config.DeviceCredentials.Windows.ClientCredential = new NetworkCredential();
                this.Config.DeviceCredentials.Windows.ClientCredential.Domain = deviceDomain;
                this.Config.DeviceCredentials.Windows.ClientCredential.UserName = deviceUserName;
                this.Config.DeviceCredentials.Windows.ClientCredential.Password = devicePassword;
                this.Config.OrganizationUri = GetOrganizationAddress(this.Config.DiscoveryUri);
                if (this.Configurations == null)
                {
                    this.Configurations = new Collection<Configuration>();
                    this.Configurations.Add(this.Config);
                }
                this.Configuration = this.Config;
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
            {
                throw;
            }
        }

        #endregion

        #region Properties

        /// <summary>
        /// Configuration list property.
        /// </summary>
        private Collection<Configuration> _configurations;

        /// <summary>
        /// Configuration list property.
        /// </summary>
        internal Collection<Configuration> Configurations
        {
            get
            {
                return _configurations;
            }
            set
            {
                _configurations = value;
            }
        }

        /// <summary>
        /// Configuration property.
        /// </summary>
        private Configuration _configuration;

        /// <summary>
        /// Configuration property.
        /// </summary>
        internal Configuration Configuration
        {
            get
            {
                return _configuration;
            }
            set
            {
                _configuration = value;
            }
        }

        /// <summary>
        /// Configuration property.
        /// </summary>
        private Configuration _config = new Configuration();

        /// <summary>
        /// Configuration property.
        /// </summary>
        private Configuration Config
        {
            get
            {
                return _config;
            }
        }

        /// <summary>
        /// Retrieve Organizations Request.
        /// </summary>
        private RetrieveOrganizationsRequest _orgRequest;

        /// <summary>
        /// Retrieve Organizations Request.
        /// </summary>
        private RetrieveOrganizationsRequest OrgRequest
        {
            get
            {
                return _orgRequest;
            }
            set
            {
                _orgRequest = value;
            }
        }

        /// <summary>
        /// Retrieve Organizations Response.
        /// </summary>
        private RetrieveOrganizationsResponse _orgResponse;

        /// <summary>
        /// Retrieve Organizations Response.
        /// </summary>
        internal RetrieveOrganizationsResponse OrgResponse
        {
            get
            {
                return _orgResponse;
            }
            set
            {
                _orgResponse = value;
            }
        }

        /// <summary>
        /// Organization Detail Collection.
        /// </summary>
        private OrganizationDetailCollection _orgs;

        /// <summary>
        /// Organization Detail Collection.
        /// </summary>
        private OrganizationDetailCollection Orgs
        {
            get
            {
                return _orgs;
            }
            set
            {
                _orgs = value;
            }
        }

        /// <summary>
        /// Organization Detail.
        /// </summary>
        private OrganizationDetail _org;

        /// <summary>
        /// Organization Detail.
        /// </summary>
        private OrganizationDetail Org
        {
            get
            {
                return _org;
            }
            set
            {
                _org = value;
            }
        }

        /// <summary>
        /// Discovery Service Proxy.
        /// </summary>
        private DiscoveryServiceProxy _serviceProxy;

        /// <summary>
        /// Discovery Service Proxy.
        /// </summary>
        private DiscoveryServiceProxy ServiceProxy
        {
            get
            {
                return _serviceProxy;
            }
            set
            {
                _serviceProxy = value;
            }
        }

        /// <summary>
        /// URI Scheme.
        /// </summary>
        private string _uriScheme;

        /// <summary>
        /// Gets or sets the URI scheme.
        /// </summary>
        /// <value>
        /// The URI scheme.
        /// </value>
        public string UriScheme
        {
            get
            {
                return _uriScheme;
            }
            set
            {
                _uriScheme = value;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Discovers the Dynamics CRM Service URL.
        /// </summary>
        /// <param name="service">Discovery service.</param>
        /// <returns>Organisation detail collection.</returns>
        internal OrganizationDetailCollection DiscoverOrganizations(IDiscoveryService service)
        {
            try
            {
                OrgRequest = new RetrieveOrganizationsRequest();
                OrgResponse = (RetrieveOrganizationsResponse)service.Execute(OrgRequest);

                return OrgResponse.Details;
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
            {
                throw;
            }
        }

        /// <summary>
        /// Finds the organisation.
        /// </summary>
        /// <param name="orgDetails">Organisation detail collection.</param>
        /// <returns>Organisation detail.</returns>
        internal OrganizationDetail FindOrganization(OrganizationDetailCollection orgDetails)
        {
            try
            {
                OrganizationDetail orgDetail = null;

                foreach (OrganizationDetail detail in orgDetails)
                {
                    if (String.Compare(detail.UniqueName, Config.OrganizationName, CultureInfo.CurrentCulture, CompareOptions.None) == 0)
                    {
                        orgDetail = detail;
                        break;
                    }
                }

                return orgDetail;
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
            {
                throw;
            }
        }

        /// <summary>
        /// Gets the organisation address.
        /// </summary>
        /// <param name="discoveryServiceUri">Discovery service URI.</param>
        /// <returns>The organisation address.</returns>
        internal Uri GetOrganizationAddress(Uri discoveryServiceUri)
        {
            try
            {
                using (ServiceProxy = new DiscoveryServiceProxy(discoveryServiceUri, null, Config.Credentials, Config.DeviceCredentials))
                {
                    if (ServiceProxy != null)
                    {
                        Orgs = DiscoverOrganizations(ServiceProxy);
                        Org = FindOrganization(Orgs);
                        if (Orgs.Count > 0)
                        {
                            return new Uri(Org.Endpoints[EndpointType.OrganizationService]);
                        }
                        else
                        {
                            return new Uri(UriScheme + "://" + Config.ServerAddress + "/" + Config.OrganizationName + "/XrmServices/2011/Organization.svc");
                        }
                    }
                    else
                    {
                        return new Uri(UriScheme + "://" + Config.ServerAddress + "/" + Config.OrganizationName + "/XrmServices/2011/Organization.svc");
                    }
                }
            }
            catch (FaultException<Microsoft.Xrm.Sdk.OrganizationServiceFault>)
            {
                try
                {
                    return new Uri(UriScheme + "://" + Config.ServerAddress + "/" + Config.OrganizationName + "/XrmServices/2011/Organization.svc");
                }
                catch
                {
                    throw;
                }
            }
        }

        #endregion
    }
}
